package com.niodata.dp.core.usermgr.op.impl;

import com.niodata.dp.core.DpConfiguration;
import com.niodata.dp.core.OwnerType;
import com.niodata.dp.core.exception.ErrorCodes;
import com.niodata.dp.core.hdfs.HdfsFileSystem;
import com.niodata.dp.core.resource.ResourceDetail;
import com.niodata.dp.core.resource.ResourceManager;
import com.niodata.dp.core.resource.ResourceMapper;
import com.niodata.dp.core.resource.ResourceType;
import com.niodata.dp.core.security.ResourceAuthMapper;
import com.niodata.dp.core.usermgr.DpUsermgrException;
import com.niodata.dp.core.usermgr.HdfsUtil;
import com.niodata.dp.core.usermgr.dao.GroupInfoMapper;
import com.niodata.dp.core.usermgr.dao.UserGroupMapper;
import com.niodata.dp.core.usermgr.dao.UserInfoMapper;
import com.niodata.dp.core.usermgr.dao.UserKeytabMapper;
import com.niodata.dp.core.usermgr.enums.GroupRole;
import com.niodata.dp.core.usermgr.enums.SystemRole;
import com.niodata.dp.core.usermgr.model.GroupInfo;
import com.niodata.dp.core.usermgr.model.UserGroup;
import com.niodata.dp.core.usermgr.model.UserInfo;
import com.niodata.dp.core.usermgr.model.UserKeytab;
import com.niodata.dp.core.usermgr.model.result.UserResult;
import com.niodata.dp.core.usermgr.op.UserService;
import com.niodata.dp.core.usermgr.op.kl.KdcLdapOperation;
import com.niodata.dp.log.LogUtil;
import com.niodata.dp.util.IdUtil;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("DpUserMgrUser")
public class UserServiceImpl implements UserService {

  private static final Logger logger = Logger.getLogger(UserServiceImpl.class);

  private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

  @Autowired
  private UserInfoMapper userInfoMapper;
  @Autowired
  private UserGroupMapper userGroupMapper;
  @Autowired
  private UserKeytabMapper userKeytabMapper;
  @Autowired
  private GroupInfoMapper groupInfoMapper;
  @Autowired
  private ResourceAuthMapper resourceAuthMapper;
  @Autowired
  private ResourceMapper resourceMapper;
  @Autowired
  @Qualifier("ResourceManagerImpl")
  private ResourceManager resourceManager;

  public UserServiceImpl() {

  }

  @Override
  public void createUser(UserInfo userInfo) throws DpUsermgrException {
    if (userInfo == null) {
      throw new DpUsermgrException(ErrorCodes.ERROR_USER_NOT_EXISTS, "Null user info.");
    }
    if (userInfo.getRole() == null
          || userInfo.getLoginName() == null
          || userInfo.getEmail() == null
          || userInfo.getPassword() == null
          || userInfo.getUserName() == null) {
      throw new DpUsermgrException(ErrorCodes.ERROR_PARAM, "Missing user info args...");
    }
    userInfo.setCreateTime(new Timestamp(System.currentTimeMillis()));
    userInfo.setHdfsUser(userInfo.getLoginName());
    userInfo.setHdfsHome(HdfsUtil.getUserHdfsHome(userInfo.getLoginName()));
    String unEncodePass = userInfo.getPassword();
    String encodedPassword = passwordEncoder.encode(unEncodePass);
    userInfo.setPassword(encodedPassword);
    if (DpConfiguration.isKerberosEnabled()) {
      try {
        byte[] keytab =
              KdcLdapOperation.getInstance()
                    .createUserAndReturnKeyTab(userInfo.getHdfsUser(), encodedPassword);
        UserKeytab userKeytab = new UserKeytab();
        userKeytab.setUserName(userInfo.getHdfsUser());
        userKeytab.setKeytab(keytab);
        userKeytab.setCreateTime(new Timestamp(new Date().getTime()));
        userKeytabMapper.insert(userKeytab);
      } catch (DpUsermgrException ex) {
        LogUtil.logStackTrace(logger, ex);
        throw new DpUsermgrException(
              ErrorCodes.ERROR_KDC, "Error occurred while creating keytab.");
      }
    }
    userInfoMapper.insert(userInfo);
    ResourceDetail resourceDetail = new ResourceDetail();
    resourceDetail.setOwnerId(userInfo.getUserId());
    resourceDetail.setOwnerType(OwnerType.USER);
    resourceDetail.setPath(userInfo.getHdfsHome());
    resourceDetail.setTypeName(ResourceType.File.getTypeName());
    resourceDetail.setCreateTime(new Date());
    resourceDetail.setId(IdUtil.uuid());
    resourceMapper.saveResource(resourceDetail);
    if (DpConfiguration.isHadoopEnabled()) {
      HdfsFileSystem.getInstance()
            .mkdir(userInfo.getHdfsHome(), userInfo.getHdfsUser(), userInfo.getUserName());
    }
  }

  @Override
  @Transactional
  public void removeUser(Long userId) throws DpUsermgrException {
    // 如果该用户是组的owner，则不允许删除
    List<UserGroup> ownerGroups =
          userGroupMapper.getUserGroupInfoByRole(userId, GroupRole.OWNER, true);
    if (ownerGroups != null && ownerGroups.size() > 0) {
      throw new DpUsermgrException(
            ErrorCodes.ERROR_PERMISSION_DENIED, "can't delete user,exist owner group...");
    }
    List<String> groupNames = new ArrayList<>();
    List<UserGroup> userGroupList = userGroupMapper.getGroupRoleList(userId, true);
    for (UserGroup userGroup : userGroupList) {
      GroupInfo groupInfo = groupInfoMapper.getGroupInfo(userGroup.getGroupId());
      String groupName = groupInfo.getHdfsGroupName();
      groupNames.add(groupName);
    }
    // remove from user_group
    UserInfo userInfo = userInfoMapper.getUserInfo(userId);
    userGroupMapper.deleteByUserId(userId);
    // remove hdfs dir
    userKeytabMapper.delete(userInfo.getHdfsUser());
    userInfoMapper.delete(userId);
    userGroupMapper.delete(userId, null);
    resourceAuthMapper.deleteResAuth(null,
          userInfo.getUserId(), OwnerType.USER.name(), null);
    List<ResourceDetail> list =
          resourceManager.getUserManagedResources(userId, ResourceType.File.getTypeName());
    for (ResourceDetail detail : list) {
      resourceAuthMapper.deleteResAuthOfResource(detail.getId());
      resourceManager.deleteResource(detail.getId());
    }
    if (DpConfiguration.isHadoopEnabled()) {
      HdfsFileSystem.getInstance().deleteDir(userInfo.getHdfsHome());
    }
    if (DpConfiguration.isKerberosEnabled()) {
      KdcLdapOperation.getInstance().removeUser(userInfo.getHdfsUser(), groupNames);
    }
  }

  @Override
  public void modifyUserRole(Long userId, SystemRole systemRole) throws DpUsermgrException {
    userInfoMapper.updateRole(userId, systemRole);
  }

  @Override
  public void modifyUserInfo(Long userId, String email, String password, String detail)
        throws DpUsermgrException {
    UserInfo userInfo = this.userInfoMapper.getUserInfo(userId);
    String encodedPassword = null;
    if (password == null || password.isEmpty()) {
      encodedPassword = userInfo.getPassword();
    } else {
      encodedPassword = passwordEncoder.encode(password);
    }
    userInfoMapper.updateInfo(userId, email, encodedPassword, detail);
    KdcLdapOperation.getInstance().changePassword(userInfo.getUserName(), encodedPassword);
  }

  @Override
  public UserInfo getUserInfo(Long userId) throws DpUsermgrException {
    return userInfoMapper.getUserInfo(userId);
  }

  @Override
  public UserInfo getUserInfo(String loginName) throws DpUsermgrException {
    return userInfoMapper.getUserInfoByLogInName(loginName);
  }

  @Override
  public List<UserInfo> getGroupUsers(Long groupId) throws DpUsermgrException {
    List<UserInfo> userInfoList = userInfoMapper.getGroupMembers(groupId);
    return userInfoList;
  }

  @Override
  public List<UserInfo> getAllUsersExcept(Long... userIds) throws DpUsermgrException {
    return this.userInfoMapper.getUserInfoList(userIds);
  }

  @Override
  public int getAllUsersByCurrentUserRoleCount(SystemRole role) throws DpUsermgrException {
    return this.userInfoMapper.getUserInfoCountBySystemRole(role);
  }

  @Override
  public List<UserResult> getAllUsersByCurrentUserRole(SystemRole role, int pageIndex, int pageSize)
        throws DpUsermgrException {
    List<UserInfo> list =
          this.userInfoMapper.getUserInfoListBySystemRole(role, (pageIndex - 1) * pageSize,
                pageSize);
    List<UserResult> ret = new ArrayList<>();
    for (UserInfo userInfo : list) {
      UserResult userResult = new UserResult();
      userResult.setUserId(userInfo.getUserId());
      userResult.setUserName(userInfo.getUserName());
      userResult.setRole(userInfo.getRole());
      userResult.setDetail(userInfo.getDetail());
      userResult.setEmail(userInfo.getEmail());
      List<GroupInfo> l = this.groupInfoMapper.getAllGroup(userResult.getUserId());
      userResult.setGroups(l);
      ret.add(userResult);
    }
    return ret;
  }

  @Override
  public UserInfo getGroupMaster(Long groupId) {
    return this.userInfoMapper.getGroupOwner(groupId);
  }

  @Override
  public byte[] getUserKeytab(String userName) throws DpUsermgrException {
    UserKeytab userKeytab = userKeytabMapper.select(userName);
    return userKeytab.getKeytab();
  }

  @Override
  public byte[] getMasterKeytab(String groupName) throws DpUsermgrException {
    String hdfsName = HdfsUtil.getHdfsGroupOwnerUser(groupName);
    UserKeytab userKeytab = userKeytabMapper.select(hdfsName);
    return userKeytab.getKeytab();
  }

  @Override
  public boolean checkIsOwnerAnyGroup(Long userId) throws DpUsermgrException {
    List<UserGroup> list = userGroupMapper.getUserGroupInfoByRole(userId, GroupRole.OWNER, true);
    return list.size() > 0;
  }

  @Override
  public List<UserInfo> searchUserByName(String nameKeyWord) {
    return userInfoMapper.searchUserByName(nameKeyWord);
  }
}
